package org.lboot.rbac.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.DesensitizedUtil;
import lombok.Data;
import org.lboot.core.auth.AuthService;
import org.lboot.core.auth.anno.CheckRole;
import org.lboot.core.domain.ErrMsg;
import org.lboot.core.domain.PageResultDTO;
import org.lboot.core.domain.ResponseDTO;
import org.lboot.core.exception.AsyncBusinessException;
import org.lboot.jpa.utils.JpaDataUtil;
import org.lboot.llog.anno.ApiLog;
import org.lboot.llog.module.ApiLogEntity;
import org.lboot.rbac.loader.PasswordGenerator;
import org.lboot.rbac.module.role.SysRole;
import org.lboot.rbac.module.role.SysRoleService;
import org.lboot.rbac.module.user.SysUser;
import org.lboot.rbac.module.user.SysUserService;
import org.lboot.rbac.module.user.map.SysUserRoleService;
import org.lboot.rbac.module.user.params.UserCreateParams;
import org.lboot.rbac.module.user.params.UserQueryParams;
import org.lboot.rbac.module.user.params.UserUpdateParams;
import org.lboot.rbac.params.PasswordResetParams;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.lboot.rbac.util.RbacUtil;
import org.lboot.starter.plugin.rsr.RsrIgnore;
import org.lboot.starter.plugin.rsr.RsrTree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author kindear
 * 用户管理控制器
 */
@Slf4j
@RestController
@RequestMapping("system")
@Api(tags = "用户管理")
@RsrTree("系统管理")
public class UserController {
    @Resource
    SysUserService userService;

    @Resource
    SysRoleService roleService;

    @Autowired
    AuthService authService;

    @Autowired
    SysUserRoleService userRoleService;

    @Autowired
    PasswordGenerator passwordGenerator;

    @Autowired
    @Qualifier("threadExecutor")
    ThreadPoolTaskExecutor threadExecutor;

    /** 新建用户 **/
    @ApiLog("RBAC")
    @PostMapping("users")
    @ApiOperation(value = "用户新建",notes = "")
    @CheckRole(orPerm = "rbac:user:create")
    public ResponseDTO<Object> userCreate(@RequestBody @Validated UserCreateParams params)   {
        SysUser sysUser = new SysUser();
        BeanUtil.copyProperties(params,sysUser,CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
        // 用户名核查
        CompletableFuture<ResponseDTO<Object>> userNameCheck = CompletableFuture.supplyAsync(()->{
            Optional<SysUser> userOp = userService.getByUserName(params.getUserName());
            if (userOp.isPresent()){
                throw new AsyncBusinessException(HttpStatus.BAD_REQUEST, ErrMsg.build("重复账户"));
                //return ResponseDTO.wrap(HttpStatus.BAD_REQUEST,"重复账户");
            }
            return null;
        });
        // 用户手机号核查
        CompletableFuture<ResponseDTO<Object>> mobileCheck = CompletableFuture.supplyAsync(()->{
            //Optional<SysUser> userOp = userService.getByMobileV1(params.getMobile());
            if (userService.isMobileExist(params.getMobile())){
                throw new AsyncBusinessException(HttpStatus.BAD_REQUEST,ErrMsg.build("重复手机号"));
                //return ResponseDTO.wrap(HttpStatus.BAD_REQUEST,"重复手机号");
            }
            return null;
        },threadExecutor);
        // 同时执行
        CompletableFuture.allOf(userNameCheck,mobileCheck).join();
        // 设置初始密码
        sysUser.setPassword(passwordGenerator.build());
        SysUser result = userService.saveOne(sysUser);
        // 部门绑定
        return ResponseDTO.succData(RbacUtil.desensitize(result));
    }




    @ApiLog("RBAC")
    @DeleteMapping("users/{userId}")
    @ApiOperation(value = "用户删除",notes = "")
    @RsrIgnore
    @CheckRole(orPerm = "rbac:user:delete")
    public ResponseDTO<Object> userDelete(@PathVariable("userId") Long userId){
        // 清除关联信息
        userRoleService.deleteByUserId(userId);
        // 清除用户
        userService.deleteOne(userId);
        return ResponseDTO.succMsg("删除成功");
    }

    @ApiLog(value = "RBAC",ignoreResponse = 1)
    @PutMapping("users")
    @ApiOperation(value = "用户更新",notes = "")
    @CheckRole(orPerm = "rbac:user:update")
    public ResponseDTO<Object> userUpdate(@RequestBody @Validated UserUpdateParams params){
        // 既不是管理员也不是自己本人 --
        if (!authService.isAdmin() && !params.getId().equals(StpUtil.getLoginIdAsLong())){
            return ResponseDTO.wrap(HttpStatus.FORBIDDEN,"权限不足");
        }
        Optional<SysUser> userOp = userService.getOne(params.getId());
        if (!userOp.isPresent()){
            return ResponseDTO.wrap(HttpStatus.NOT_FOUND);
        }
        SysUser user = userOp.get();
        BeanUtil.copyProperties(params,user,CopyOptions.create().setIgnoreError(true).setIgnoreNullValue(true));
        SysUser result = userService.updateOne(user);
        return ResponseDTO.succData(RbacUtil.desensitize(result));
    }



    @GetMapping("users")
    @ApiOperation(value = "用户列表",notes = "")
    public ResponseDTO<Object> userListQuery(UserQueryParams params){

        // tips : 页码对应 - 1
        Pageable pageable = JpaDataUtil.buildPageable(params);
        // 如果参数为空
        Specification<SysUser> spec = (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            // 构建 pr 模糊 查询
            if (Validator.isNotEmpty(params.getSearchKey())){
                Predicate p1 = cb.like(root.get("nickName"),   params.getSearchKey() + "%");

                Predicate p2 = cb.like(root.get("mobile"),   params.getSearchKey() + "%");

                Predicate p3 = cb.like(root.get("userName"),   params.getSearchKey() + "%");

                list.add(cb.or(p1,p2,p3));
            }
            // 基于状态查询
            if (Validator.isNotEmpty(params.getStatus())){
                Predicate p1 = cb.equal(root.get("status"),params.getStatus());
                list.add(p1);
            }
            // 如果部门信息不为空
            if (Validator.isNotEmpty(params.getDeptId())){
                Predicate p2 = cb.equal(root.get("deptId"),params.getDeptId());
                list.add(p2);
            }
            // 账户类型
            if (Validator.isNotEmpty(params.getUserType())){
                Predicate p3 = cb.equal(root.get("userType"),params.getUserType());
                list.add(p3);
            }
            Predicate[] predicates = new Predicate[list.size()];
            return cb.and(list.toArray(predicates));
        };
        // 隐藏密码
        PageResultDTO<SysUser> resultDTO =  userService.getAll(pageable,spec);
        List<SysUser> users = resultDTO.getContent();
        users = users.stream().peek(ele-> ele.setPassword("******")).collect(Collectors.toList());
        resultDTO.setContent(users);
        return ResponseDTO.succData(
               resultDTO
        );
    }

    @GetMapping("users/{id}")
    @ApiOperation(value = "用户查询",notes = "")
    public ResponseDTO<Object> userQuery(@PathVariable("id") Long id){
        SysUser user = userService.getOneUnwrap(id);
        return ResponseDTO.succData(RbacUtil.desensitize(user));
    }

    @GetMapping("users/{id}/roles")
    @ApiOperation(value = "用户关联角色")
    public ResponseDTO<Object> userQueryRoles(@PathVariable("id") Long id){
        return ResponseDTO.succData(
                userRoleService.getRoles(id)
        );
    }



    @GetMapping("users/{userId}/password")
    @ApiOperation(value = "获取密码")
    @CheckRole(orPerm = "user:password:query")
    public ResponseDTO<Object> getPwd(@PathVariable("userId") Long userId){
        // 如果是当前登录用户或者管理员
        SysUser user = userService.getOneUnwrap(userId);
        if (StpUtil.hasRole(userId,"admin")){
            return ResponseDTO.wrap(HttpStatus.FORBIDDEN,"无权操作【admin】");
        }
        return ResponseDTO.succData(
                user.getPassword()
        );
    }

    @PutMapping("users/{userId}/password")
    @ApiOperation(value = "密码重置",notes = "")
    @CheckRole(orPerm = "user:password:reset")
    public ResponseDTO<Object> resetPwd(@PathVariable("userId") Long userId, @Validated @RequestBody PasswordResetParams params){
        SysUser user = userService.getOneUnwrap(userId);
        if (StpUtil.hasRole(userId,"admin")){
            return ResponseDTO.wrap(HttpStatus.FORBIDDEN,"无权操作【admin】");
        }
        user.setPassword(params.getPassword());
        userService.updateOne(user);
        return ResponseDTO.succMsg("重置成功");
    }
}
